<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <title>音乐粒子效果</title>
    <style>
        body {
            margin: 0;
            background-color: #000;
            overflow: hidden;
        }
        canvas {
            display: block;
        }
    </style>
</head>

<body>
    <canvas id="canvas"></canvas>
   
    <audio id="audio" src="2.mp3" loop controls style="
    position: fixed;
    bottom: 20px;
    left: 50%;
    transform: translateX(-50%);
    z-index: 1000;
    width: 300px;
"></audio>

    <script>
    
        const canvas = document.getElementById('canvas');
        const ctx = canvas.getContext('2d');
        const audio = document.getElementById('audio');
        
        // 设置画布大小
        canvas.width = window.innerWidth;
        canvas.height = window.innerHeight;
        
        window.addEventListener('resize', () => {
            canvas.width = window.innerWidth;
            canvas.height = window.innerHeight;
        });
        
        // 粒子类
        class Particle {
            constructor(x, y, color) {
                this.x = x;
                this.y = y;
                this.originX = x;
                this.originY = y;
                this.size = 2;
                this.color = color;
                this.vx = 0;
                this.vy = 0;
                this.friction = 0.95;
                this.elasticity = 0.5;
                this.distance = 100;
            }
        
            draw() {
                ctx.fillStyle = this.color;
                ctx.beginPath();
                ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
                ctx.closePath();
                ctx.fill();
            }
        
            update() {
                let dx = this.originX - this.x;
                let dy = this.originY - this.y;
                this.vx += dx / 100;
                this.vy += dy / 100;
                this.vx *= this.friction;
                this.vy *= this.friction;
                this.x += this.vx;
                this.y += this.vy;
                this.draw();
            }
        }
        
        // 初始化粒子
        const particles = [];
        const particleCount = 1000;
        const colors = [
            '#6a0dad', // 紫色
            '#7a1fa2',
            '#8a1fb3',
            '#9a20c4',
            '#aa21d5'
        ];
        
        for (let i = 0; i < particleCount; i++) {
    const x = Math.random() * canvas.width * 0.7; // 限制在画布左侧70%区域
    const y = Math.random() * canvas.height;
    const color = colors[Math.floor(Math.random() * colors.length)];
    particles.push(new Particle(x, y, color));
}
        
        // 音频分析
        const audioContext = new (window.AudioContext || window.webkitAudioContext)();
        const analyser = audioContext.createAnalyser();
        const source = audioContext.createMediaElementSource(audio);
        source.connect(analyser);
        analyser.connect(audioContext.destination);
        analyser.fftSize = 256;
        const bufferLength = analyser.frequencyBinCount;
        const dataArray = new Uint8Array(bufferLength);
        
        // 动画循环
        function animate() {
            requestAnimationFrame(animate);
            analyser.getByteFrequencyData(dataArray);
        
            // 清除画布
            ctx.clearRect(0, 0, canvas.width, canvas.height);
        
 // 新增进度环绘制
 const progress = audio.currentTime / audio.duration || 0;
    const centerX = canvas.width * 0.3;
    const centerY = canvas.height / 2;
    
    // 背景环
    ctx.beginPath();
    ctx.arc(centerX, centerY, 160, 0, Math.PI * 2);
    ctx.strokeStyle = 'rgba(255,255,255,1)';
    ctx.lineWidth = 14;
    ctx.stroke();

    // 进度环
    ctx.beginPath();
    ctx.arc(centerX, centerY, 150, 0, Math.PI * 2 * progress);
    ctx.strokeStyle = 'rgba(255,255,255,0.8)';
    ctx.lineWidth = 20;
    ctx.shadowColor = 'white';
    ctx.shadowBlur = 15;
    ctx.stroke();
    
    // 重置阴影效果
    ctx.shadowColor = 'transparent';
    ctx.shadowBlur = 0;


            // 计算平均频率
            const average = dataArray.reduce((a, b) => a + b, 0) / bufferLength;
        
            // 更新粒子位置
            particles.forEach((particle, index) => {
        const freq = dataArray[index % bufferLength];
        const radius = 200 + freq * 0.5;
        // 将中心点左移30%
        const centerX = canvas.width * 0.3; 
        const angle = Math.atan2(particle.originY - canvas.height / 2, particle.originX - centerX);
        const x = centerX + radius * Math.cos(angle);
        const y = canvas.height / 2 + radius * Math.sin(angle);
                particle.originX = x;
                particle.originY = y;
        
                // 根据频率变化粒子大小
                particle.size = 10 + freq * 0.05;
        
                particle.update();
            });
        }
        
        audio.play();
        animate();
    </script>

    <script>
        // 生成球体表面的粒子
        const radius = 100;
        const particleCount = 1000;
        for (let i = 0; i < particleCount; i++) {
            const theta = Math.random() * Math.PI * 2;
            const phi = Math.acos(2 * Math.random() - 1);
            const x = radius * Math.cos(theta) * Math.sin(phi) + canvas.width / 2;
            const y = radius * Math.sin(theta) * Math.sin(phi) + canvas.height / 2;
            const color = colors[Math.floor(Math.random() * colors.length)];
            particles.push(new Particle(x, y, color));
        }
    </script>
   
</body>

</html>